iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 20
0
Software Development

邁向專業軟體工程師必修的英文課系列 第 20

Day 20 - [連接詞二] 想好好討論一下if的用法 - 2

  • 分享至 

  • xImage
  •  

昨天提到了if常見問題有判斷式太長無限增生的else if,今天接著說if常見問題。

巢狀的if

是否有看過像被鑽地機光顧過的巢狀if?
https://blog.jetbrains.com/wp-content/uploads/2017/09/idea-05-extract-has-more-elements-prev.png
cc jetbrains
比起前兩種,其實這個比較常見,而且都是發生在legacy code居多:增加的條件或修改導致程式必需增生更多條件來達成或規避掉某些情況。但總結而言還是有些可以學習的地方,會發生的原因除了因應新的規則與條件外,程式沒有及時進行調整造成技術債也是主因:雖然if可以解決問題,但如果宏觀來看,這個程式的結構己經被破壞掉了。
怎麼處理?有三個方法:

  1. 試著把if條件結合,消除掉巢狀結構。
  2. 將if條件重構成方法提出方法外,但這個做法只是掩耳盜鈴,其實沒什麼改善。
  3. 重構整個方法,讓這個方法結構完整。

通常一個方法裡不要超過3個if判斷式,如果有超過三個if,有很大的機率是這個方法是寫錯的。

方法裡有太多的if

這個問題跟上面一個很像,程式裡有太多的if-else-if。以下我引用一段從jetbrains裡節錄的程式碼

    //cc jetbrains https://blog.jetbrains.com/idea/2017/09/code-smells-if-statements/
    if (mf.isPresent() && mf.get().isMap()) {
        //skip the map key validation, and move to the next fieldName
        i++;
    }
    if (i >= pathElements.length) {
        break;
    }
    if (!fieldIsArrayOperator) {
        //catch people trying to search/update into @Reference/@Serialized fields
        if (validateNames && (mf.get().isReference() || mf.get().hasAnnotation(Serialized.class))) {
            throw cannotQueryPastFieldException(propertyPath, fieldName, validatedField);
        }
        if (!mf.isPresent() && validatedField.mappedClass.isInterface()) {
            break;
        } else if (!mf.isPresent()) {
            throw fieldNotFoundException(propertyPath, validatedField);
        }
        //get the next MappedClass for the next field validation
        MappedField mappedField = mf.get();
        validatedField.mappedClass = mapper.getMappedClass((mappedField.isSingleValue()) ? mappedField.getType() : mappedField.getSubClass());
    }

這段大約二十行的程式碼,雖然感覺做了很多事,但如果仔細讀的話,這程式什麼也沒做:中間有四個條件會讓程式跳離或拋出例外,有一處是個計數器,真正有做事的在倒數兩行:

    MappedField mappedField = mf.get();
    validatedField.mappedClass = mapper.getMappedClass((mappedField.isSingleValue()) ? mappedField.getType() : mappedField.getSubClass());

所以可以說這個方法就像是個長滿了芒草的營地,但卻要說服大家在這裡安營扎寨,結果只有兩種:一個大家要花更多心力找到可以休息的地方,另一個是大家要花更多時間把營地整理好。
重構就是為了處理類似的問題:怎麼把相關的if移到更適合的位置,怎麼把責任歸到更適合的類別裡,怎麼重寫讓程式更簡潔。
而不是用包成方法、寫註解或者用#region的方法,把程式碼藏起來,變成一個好像很簡潔的程式碼:手總是要用髒的,那不如現在就開始動手做?

包了比方法還要多的程式碼

是否有看過一個方法,用if又另外多包了一層,感覺就像是日式的過度包裝一樣?

public UserGift CreateGiftForUser(User userInfo){
    var gift = new UserGift(userInfo);
    if(userInfo.Level == Level.VIP || userInfo.Affiliate == "ITHelp"){
        gift.AddPoint(1000);
        gift.ExtendExpireDate(100);
        gift.AddExclusiveFrame();
        gift.SendThankYouLetter();
        gift.PushThankYouNotification();
        gift.GiveSpecialSurprise();
        gift.SendWhateverTheyWant();
        ....
        ....
        ....
        ....
    }
    return gift;
}

其實程式碼也沒什麼錯,但如果不符合條件的話,程式碼就一行。其實可以理解有些人喜歡合併程式碼,不喜歡有太多的方法遍佈在程式裡,但其實適度的分割權責,讓流程各自去運行,無論在除錯或讀碼時都會更流暢。
那這段程式碼應該怎麼改?

public UserGift CreateGiftForUser(User userInfo){
    var gift = new UserGift(userInfo);
    if(userInfo.Level != Level.VIP && userInfo.Affiliate != "ITHelp") return gift;
    gift.AddPoint(1000);
    gift.ExtendExpireDate(100);
    gift.AddExclusiveFrame();
    gift.SendThankYouLetter();
    gift.PushThankYouNotification();
    gift.GiveSpecialSurprise();
    gift.SendWhateverTheyWant();
    ....
    ....
    ....
    ....
    return gift;
}

或者在UserGift裡增加一個GiveSpecialGift,處理特殊條件。

關於濫用if的問題還有很多可以寫,我自己也開過幾篇文章在寫if的重構,這對我來說是個很不錯的挑戰,也在其中學到了不少東西。
你有看過什麼很特別的if判斷式嗎?或者有什麼特別的處理方法嗎?分享給大家知道吧!

Code Smells: If Statements
倒底要不要寫註解?從重構四個if/else說起


這篇文章不是預存的文章,也不是部落格分享文,更不是上班偷打的文章。


上一篇
Day 19 - [連接詞一] 想好好討論一下if的用法 - 1
下一篇
Day 21 - [連接詞三] And and Or,And or Or ?
系列文
邁向專業軟體工程師必修的英文課30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言